#ifndef VECTOR_H
#define VECTOR_H

#include <cstring>

template <typename T>
T _min(const T &a, const T &b)
{
    return (a > b ? b : a);
}

template <typename T>
class Vector {
public:
    Vector()
    {
        init(reinterpret_cast<T*>(operator new(sizeof (T) * 10)), 0, 10);
    }
    Vector(const Vector& vec)
    {
        init(copy(vec.m_array, vec.m_length, vec.m_capacity, true), vec.m_length, vec.m_capacity);
    }
    Vector& operator=(const Vector& vec)
    {
        if (this != &vec)
        {
            update(copy(vec.m_array, vec.m_length, vec.m_capacity, true), vec.m_length, vec.m_capacity, true);
        }
        return *this;
    }
    ~Vector()
    {
        for (int i = 0; i < m_length; i++)
        {
            m_array[i].~T();
        }
        operator delete(m_array);
    }

    void clear()
    {
        this->~Vector<T>();
        this->init(reinterpret_cast<T*>(operator new(sizeof (T) * 10)), 0, 10);
    }
    void insert(int index, const T &obj)
    {
        if (0 <= index && index <= m_length)
        {
            if (m_capacity > m_length)
            {
                for (int i = m_length; i > index; i--)
                {
                    memcpy(m_array + i, m_array + i - 1, sizeof(T));
                }
                m_length++;
                new (m_array + index) T(obj);
            }
            else
            {
                T *ret = copy(m_array, index, m_length * 2, false);
                new (ret + index) T(obj);
                for (int i = index; i < m_length; i++)
                {
                    memcpy(ret + i + 1, m_array + i, sizeof(T));
                }
                T * toDel = m_array;
                init(ret, m_length + 1, m_length * 2);
                operator delete(toDel);

            }
        }
        else
        {
            throw "cannot insert here";
        }
    }

    void remove(int index)
    {
        if (0 <= index && index < m_length)
        {
            m_array[index].~T();
            if (m_capacity > 4 * m_length)
            {
                int capacity = (2 * m_length > 10 ? 2 * m_length : 10);
                T *ret = copy(m_array, index, capacity, false);
                for (int i = index; i < m_length; i++)
                {
                    memcpy(ret + i, m_array + i + 1, sizeof(T));
                }
                T * toDel = m_array;
                init(ret, m_length - 1, capacity);
                operator delete(toDel);
            }
            else
            {
                for (int i = index; i < m_length - 1; i++)
                {
                    memcpy(m_array + i, m_array + i + 1, sizeof(T));
                }
                m_length--;
            }

        }
        else
        {
            throw "cannot insert here";
        }
    }

    int length()
    {
        return m_length;
    }

    int capacity()
    {
        return m_capacity;
    }

    void resize(int newCapacity)
    {
        if (m_capacity == newCapacity)
            return;

        T *ret = copy(m_array, m_length, newCapacity, false);
        if (newCapacity < m_length)
        {
            for (int i = newCapacity; i < m_length; i++)
            {
                m_array[i].~T();
            }
        }
        update(ret, _min(m_length, newCapacity), newCapacity, false);
    }

    void shrinkToFit()
    {
        if (m_length < m_capacity)
        {
            update(copy(m_array, m_length, m_length, false), m_length, m_length, false);
        }
    }

    T get(int i)
    {
        return operator[](i);
    }
    void set(int i, T value)
    {
        if (0 <= i && i < m_length)
        {
            m_array[i] = value;
        }
        else
        {
            throw "out of range";
        }
    }
    T& operator[](int i)
    {
        if (0 <= i && i < m_length)
        {
            return m_array[i];
        }
        else
        {
            throw "out of range";
        }
    }
    T operator[](int i) const
    {
        if (0 <= i && i < m_length)
        {
            return m_array[i];
        }
        else
        {
            throw "out of range";
        }
    }


protected:
    int m_length;
    int m_capacity;
    T *m_array;
    void init(T *array, int length, int capacity)
    {
        if (array != nullptr)
        {
            m_array = array;
            m_length = length;
            m_capacity = capacity;
        }
        else
        {
            throw "cannot init";
        }
    }
    T* copy(T *array, int length, int capacity, bool isCopyConstruct) // 拷贝构造
    {
        T *ret = reinterpret_cast<T*>(operator new(sizeof (T) * capacity));
        if (ret != nullptr)
        {
            int size = (length > capacity ? capacity : length);
            for (int i = 0; i < size; i++)
            {
                if (isCopyConstruct)
                {
                    new(ret + i) T(array[i]);
                }
                else
                {
                    memcpy(ret + i, array + i, sizeof(T));
                }
            }
        }
        else
        {
            throw "cannot init";
        }
        return ret;
    }

    void update(T *array, int length, int capacity, bool isDeconstruct)
    {
        if (array != nullptr)
        {
            T* temp = m_array;
            int oldLength = m_length;
            m_array = array;
            m_length = length;
            m_capacity = capacity;
            if (isDeconstruct)
            {
                for (int i = 0; i < oldLength; i++)
                {
                    temp[i].~T();
                }
            }

            operator delete(temp);
        }
        else
        {
            throw "cannot init";
        }
    }

};

#endif // VECTOR_H
